/*    
 * Copyright (c) 2013 ISoftStone, Inc.
 * All right reserved.
 *
 * 文件名：      ChargeOffServiceImpl.java
 * 作者:     Jacky Wang
 * 创建日期： 2013-7-30 下午01:51:41
 * 版本：           
 *
 */
package com.isoftstone.industry.aaa.core.service.accounting.impl;

import java.sql.Timestamp;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.isoftstone.industry.persistance.bean.Account;
import com.isoftstone.industry.persistance.bean.AccountBook;
import com.isoftstone.industry.persistance.bean.AccountItem;
import com.isoftstone.industry.persistance.bean.Bill;
import com.isoftstone.industry.persistance.bean.BillItem;
import com.isoftstone.industry.persistance.bean.BillItemAcctItem;
import com.isoftstone.industry.persistance.bean.BillingCycle;
import com.isoftstone.industry.persistance.bean.ChargeOffDetail;
import com.isoftstone.industry.persistance.bean.ProductInstance;
import com.isoftstone.industry.persistance.bean.SpecialAccountBookItem;
import com.isoftstone.industry.persistance.bean.User;
import com.isoftstone.industry.aaa.core.log.BusinessLogger;
import com.isoftstone.industry.aaa.core.log.ILogMessages;
import com.isoftstone.industry.aaa.core.service.accounting.IChargeOffService;
import com.isoftstone.industry.aaa.core.service.assist.IAccountBookService;
import com.isoftstone.industry.aaa.core.service.assist.IAccountItemService;
import com.isoftstone.industry.aaa.core.service.assist.IAccountService;
import com.isoftstone.industry.aaa.core.service.assist.IBillItemService;
import com.isoftstone.industry.aaa.core.service.assist.IBillService;
import com.isoftstone.industry.aaa.core.service.assist.IChargeOffDetailService;
import com.isoftstone.industry.aaa.core.service.assist.IProductInstService;
import com.isoftstone.industry.aaa.core.service.assist.IReceiptDisburseStatementService;
import com.isoftstone.industry.aaa.core.service.assist.ISpecialAccountBookItemService;
import com.isoftstone.industry.aaa.exception.AAAException;
import com.isoftstone.industry.aaa.exception.IAAAErrorCode;
import com.isoftstone.modules.core.utils.DateUtils;

/**
 * @author jacky
 */
@Service
public class ChargeOffServiceImpl implements IChargeOffService
{
	@Resource
	private BusinessLogger			logger;
	
	@Resource
	private IAccountItemService				acctItemService;

	@Resource
	private ISpecialAccountBookItemService	spAcctBookItemService;

	@Resource
	private IAccountBookService				acctBookService;

	@Resource
	private IChargeOffDetailService			chargeOffDetailService;

	@Resource
	private IAccountService					accountService;
	
	@Resource
	private IBillService billService;
	
	@Resource
	private IBillItemService billItemService;
	
	@Resource
	private IProductInstService prodInstService;
	
	@Resource
	private IReceiptDisburseStatementService rdsService;
	
	@Override
	@Transactional
	public void chargeOff(Bill bill) throws AAAException
	{
		//判断该用户的账户是否已冻结
		Account account = accountService.findByUser(bill.getUser().getId());
		if (account.getStatus() == Account.Status.FREEZE)
		{
			logger.info(ILogMessages.CHARGEOFF_ACCOUNT_FREEZE, bill.getUser().getUsername());
			throw new AAAException(IAAAErrorCode.ERROR_ACCOUNT_FREEZE);
		}
		List<BillItem> billItems = bill.getBillItems();
		for (BillItem billItem : billItems)
		{
			chargeOff4BillItem(billItem, account, bill);
		}
		billService.update(bill);
	}

	public void chargeOff4BillItem(BillItem billItem, Account account, Bill bill)
	{
		double paidAmount = 0L;
		List<BillItemAcctItem> baItems = billItem.getBillAcctItems();
		for (BillItemAcctItem baItem : baItems)
		{
			AccountItem acctItem = baItem.getAccountItem();
			double payableAmount = acctItem.getPayableAmount();
			if (payableAmount > 0)
			{
				double lastPaidAmount = acctItem.getPaidAmount();
				acctItem = chargeOff4AcctItem(acctItem, account);
				paidAmount += (acctItem.getPaidAmount()-lastPaidAmount);
			} else
			{
				//如果该帐目金额为0且为未销帐状态，则直接销帐
				if (AccountItem.Status.NON_CHARGE_OFF.equals(acctItem.getStatus()))
				{
					acctItemService.changeStatus(acctItem.getId(), AccountItem.Status.CHARGED_OFF);
				}
			}
		}
		bill.setPaidAmount(bill.getPaidAmount()+paidAmount);
		bill.setPayableAmount(bill.getPayableAmount()-paidAmount);
	}

	public AccountItem chargeOff4AcctItem(AccountItem acctItem, Account account)
	{
		//若该帐目已销帐，则直接忽略
		if(acctItem.getStatus().intern() == AccountItem.Status.CHARGED_OFF){
			return acctItem;
		}
		double payableAmount = acctItem.getPayableAmount();
		double paidAmount = acctItem.getPaidAmount();
		if (payableAmount > 0)
		{
			String prodId = acctItem.getProductInst().getProduct().getId();
			String prodInstId = acctItem.getProductInst().getId();
			String userName = account.getUser().getUsername();
			BillingCycle billingCycle = acctItem.getBillingCycle();
			Date date = acctItem.getCreatedTime();
			if (billingCycle != null)
			{
				date = billingCycle.getEndDate();
			}
			//先从专款专用账本支付
			AccountBook spAcctBook = acctBookService.findSpecialAcctBook(account.getId());
			//先从产品实例指定的赠送类型的专款专用中去扣费
			List<SpecialAccountBookItem> spItems = spAcctBookItemService.findEffectGivenItemBySource(spAcctBook.getId(), SpecialAccountBookItem.Source.PRODUCT_INSTANCE, prodInstId, date);
			
			if (spItems != null && spItems.size() > 0)
			{
				for (SpecialAccountBookItem spItem : spItems)
				{
					double paid = pay4SpItem(spItem, payableAmount, account, spAcctBook, acctItem, userName);
					//更新需支付的金额
					payableAmount -= paid;
					//更新已支付的金额
					paidAmount += paid;
				}
			}
			
			//如果使用产品实例指定的赠送类型的专款专用没有支付清，则使用产品相关的产品的赠送类型的专款专用进行支付
			if(payableAmount > 0){
				spItems = spAcctBookItemService.findEffectGivenItemBySource(spAcctBook.getId(), SpecialAccountBookItem.Source.PRODUCT, prodId, date);
				if (spItems != null && spItems.size() > 0)
				{
					for (SpecialAccountBookItem spItem : spItems)
					{
						double paid = pay4SpItem(spItem, payableAmount, account, spAcctBook, acctItem, userName);
						//更新需支付的金额
						payableAmount -= paid;
						//更新已支付的金额
						paidAmount += paid;
					}
				}
			}
			//如果没有支付完成，则使用产品实例指定的预付类型的专款专用进行支付
			if(payableAmount > 0){
				spItems = spAcctBookItemService.findEffectPrepaidItemBySource(spAcctBook.getId(), SpecialAccountBookItem.Source.PRODUCT_INSTANCE, prodInstId, date);
				if (spItems != null && spItems.size() > 0)
				{
					for (SpecialAccountBookItem spItem : spItems)
					{
						double paid = pay4SpItem(spItem, payableAmount, account,spAcctBook, acctItem, userName);
						//更新需支付的金额
						payableAmount -= paid;
						//更新已支付的金额
						paidAmount += paid;
					}
				}
			}
			
			//如果没有支付完成，则使用产品指定的预付类型的专款专用进行支付
			if(payableAmount > 0){
				spItems = spAcctBookItemService.findEffectPrepaidItemBySource(spAcctBook.getId(), SpecialAccountBookItem.Source.PRODUCT, prodId, date);
				if (spItems != null && spItems.size() > 0)
				{
					for (SpecialAccountBookItem spItem : spItems)
					{
						double paid = pay4SpItem(spItem, payableAmount, account,spAcctBook, acctItem, userName);
						//更新需支付的金额
						payableAmount -= paid;
						//更新已支付的金额
						paidAmount += paid;
					}
				}
			}
			
			acctBookService.update(spAcctBook);
			
			//判断若没有支付完成，使用现金账本支付
			if (payableAmount > 0)
			{
				AccountBook cashAcctBook = acctBookService.findCashAcctBook(account.getId());
				//现金账本余额
				double balance = cashAcctBook.getBalance();
				double paid = 0L;
				//如果余额大于等于需付金额
				if(balance >= payableAmount){
					paid = payableAmount;
				}else{
					paid = balance;
				}
				//更新账本余额
				cashAcctBook.setBalance(balance-paid);
				//更新需支付的金额
				payableAmount -= paid;
				//更新已支付的金额
				paidAmount += paid;
				//创建销帐记录
				if (paid > 0)
				{
					logger.info(ILogMessages.CHARGEOFF_ACCOUNT_CASH_FEE, account.getUser().getUsername(), acctItem.getSn(), paid);
					createChargeOffDetail(account, cashAcctBook, acctItem, null, paid);
					logger.info(ILogMessages.CHARGEOFF_CREATE_DETAIL, account.getUser().getUsername(), acctItem.getSn(), paid);
				}
				//更新现金账本、帐目实体
				acctBookService.update(cashAcctBook);
			}
			
			//更新帐目信息
			acctItem.setPaidAmount(paidAmount);
			acctItem.setPayableAmount(payableAmount);
			if(payableAmount > 0){
				//更新帐目信息,由于还没有付清，所以该帐目未销帐
				acctItem.setStatus(AccountItem.Status.NON_CHARGE_OFF);
			}else{
				acctItem.setStatus(AccountItem.Status.CHARGED_OFF);
			}
			acctItem.setStatusTime(DateUtils.getCurrentTimestamp());
			acctItem = acctItemService.update(acctItem);
		}else if(payableAmount == 0){
			acctItem.setStatus(AccountItem.Status.CHARGED_OFF);
			acctItem.setStatusTime(DateUtils.getCurrentTimestamp());
			acctItem = acctItemService.update(acctItem);
		}

		return acctItem;
	}

	private double pay4SpItem(SpecialAccountBookItem spItem, double payableAmount, Account account, AccountBook acctBook, AccountItem acctItem, String userName)
	{
		double paid = 0.00;
		//如果已经没有需要支付的费用，则跳出循环
		if (payableAmount == 0)
		{
			return paid;
		}
		//可以余额
		double balance = spItem.getBalance();
		//锁定金额
		double freezeBalance = spItem.getFreezeBalance();
		double totalBalance = balance + freezeBalance;
		if (totalBalance > 0)
		{
			//获取可以支付的金额，先从锁定金额支付
			if(freezeBalance >= payableAmount){
				paid = payableAmount;
				spItem.setFreezeBalance(freezeBalance-paid);
			}else{
				paid = (totalBalance >= payableAmount ? payableAmount : totalBalance);
				spItem.setFreezeBalance(0.00);
				//更新专款专用项的余额
				spItem.setBalance(totalBalance-paid);
			}
			spItem.setModifiedTime(DateUtils.getCurrentTimestamp());
			spAcctBookItemService.update(spItem);
			logger.info(ILogMessages.CHARGEOFF_ACCOUNT_SP_FEE, userName, spItem.getSn(), acctItem.getSn(), paid);
			acctBook.setBalance(acctBook.getBalance()-paid);
			//创建销帐记录
			createChargeOffDetail(account, acctBook, acctItem, spItem, paid);
			logger.info(ILogMessages.CHARGEOFF_CREATE_DETAIL, userName, acctItem.getSn(), paid);

		}
		return paid;
	}
	
	private ChargeOffDetail createChargeOffDetail(Account account, AccountBook acctBook, AccountItem acctItem, SpecialAccountBookItem spAcctBookItem, double paid)
	{
		ChargeOffDetail detail = new ChargeOffDetail();
		detail.setAccountBook(acctBook);
		detail.setAccountItem(acctItem);
		detail.setAmount(paid);
		detail.setSpAcctBookItem(spAcctBookItem);
		detail.setStatus(ChargeOffDetail.STATUS.CHARGED_OFF);
		Timestamp now = DateUtils.getCurrentTimestamp();
		detail.setCreatedTime(now);
		detail.setStatusTime(now);
		detail.setSn(DateUtils.getCurrentTime(DateUtils.YYYY_MM_DD_HH_MM_SS_SSS_PATTERN));
		//记录支出流水
		double balance = acctBook.getBalance();;
		StringBuffer memoBuff = new StringBuffer();
		StringBuffer memo1Buff = new StringBuffer();
		BillingCycle billingCycle = acctItem.getBillingCycle();
		if (spAcctBookItem != null)
		{
			memoBuff.append("专款专用账本[SN:");
			memoBuff.append(spAcctBookItem.getSn());
			memoBuff.append("]为产品实例[SN:");
			memoBuff.append(acctItem.getProductInst().getSn());
			memoBuff.append(",产品名称：");
			memoBuff.append(acctItem.getProductInst().getProduct().getName());
			memoBuff.append("]消费产生的帐目[SN:");
			memoBuff.append(acctItem.getSn());
			memoBuff.append("]支出");
			
			memo1Buff.append("为已办业务[");
			memo1Buff.append(acctItem.getProductInst().getProduct().getName());
			memo1Buff.append("]");
			if(billingCycle != null){
				memo1Buff.append("在计费周期[");
				memo1Buff.append(billingCycle.getName());
				memo1Buff.append("]内");
			}
			memo1Buff.append("产生的费用支出,相关业务编号为：");
			memo1Buff.append(acctItem.getProductInst().getSn());
			memo1Buff.append(",支出的专款专用账本为：");
			memo1Buff.append(spAcctBookItem.getSn());
		} else
		{
			memoBuff.append("为产品实例[SN:");
			memoBuff.append(acctItem.getProductInst().getSn());
			memoBuff.append(",产品名称：");
			memoBuff.append(acctItem.getProductInst().getProduct().getName());
			memoBuff.append("]消费产生的帐目[SN:");
			memoBuff.append(acctItem.getSn());
			memoBuff.append("]支出");
			
			memo1Buff.append("为已办业务[");
			memo1Buff.append(acctItem.getProductInst().getProduct().getName());
			memo1Buff.append("]");
			if(billingCycle != null){
				memo1Buff.append("在计费周期[");
				memo1Buff.append(billingCycle.getName());
				memo1Buff.append("]内");
			}
			memo1Buff.append("产生的费用支出,相关业务编号为：");
			memo1Buff.append(acctItem.getProductInst().getSn());
		}
		rdsService.checkInDisburse(account.getId(), acctBook.getId(), paid, balance, memoBuff.toString(), memo1Buff.toString());
		return chargeOffDetailService.save(detail);
	}

	@Override
	@Transactional
	public void chargeOff4Recharge(String userId)
	{
		//发现可以启用的产品实例ID
		Set<ProductInstance> prodInstSet = new HashSet<ProductInstance>();
		Account account = accountService.findByUser(userId);
		User user = account.getUser();
		logger.info(ILogMessages.CHARGEOFF4RECHARGE_START, user.getUsername());
		//先支付用户没有支付清的账单
		List<Bill> billList = billService.findPayableBill(userId);
		for (Bill bill : billList)
		{
			logger.info(ILogMessages.CHARGEOFF4RECHARGE_BILL_START, user.getUsername(), bill.getSn());
			List<BillItem> billItems = billItemService.findNeedPaidItems(bill.getId());
			for (BillItem billItem : billItems)
			{
				chargeOff4BillItem(billItem, account, bill);
				if (isChargedOff4BillItem(billItem))
				{
					prodInstSet.add(billItem.getProdInst());
				}
			}
			billService.update(bill);
			logger.info(ILogMessages.CHARGEOFF4RECHARGE_BILL_END, user.getUsername(), bill.getSn());
		}

		//支付欠费的预付款帐目
		List<AccountItem> prePaidItems = acctItemService.findOwningPrepaidItems(userId);
		for (AccountItem acctItem : prePaidItems)
		{
			logger.info(ILogMessages.CHARGEOFF4RECHARGE_ACCTITEM_START, user.getUsername(), acctItem.getSn());
			acctItem = chargeOff4AcctItem(acctItem, account);
			if (acctItem.getPayableAmount() == 0)
			{
				prodInstSet.add(acctItem.getProductInst());
			} else
			{
				prodInstSet.remove(acctItem.getProductInst());
			}
			logger.info(ILogMessages.CHARGEOFF4RECHARGE_ACCTITEM_END, user.getUsername(), acctItem.getSn());
		}
		
		//启用已经支付清的产品实例
		for (ProductInstance prodInst : prodInstSet)
		{
			prodInstService.enable(prodInst.getId());
			logger.info(ILogMessages.AUTHORIZATION_DISABLE_PRODINST, user.getUsername(), prodInst.getSn());
		}
		logger.info(ILogMessages.CHARGEOFF4RECHARGE_END, user.getUsername());
	}
	
	private boolean isChargedOff4BillItem(BillItem billItem)
	{
		List<BillItemAcctItem> baItems = billItem.getBillAcctItems();
		for (BillItemAcctItem baItem : baItems)
		{
			AccountItem acctItem = baItem.getAccountItem();
			if (!AccountItem.Status.CHARGED_OFF.equals(acctItem.getStatus()))
			{
				return false;
			}
		}
		return true;
	}

	@Transactional
	@Override
	public AccountItem chargeOff4AcctItem(String acctItemId)
	{
		AccountItem acctItem = acctItemService.findOne(acctItemId);
		User user = acctItem.getProductInst().getUser();
		Set<Account> accounts = user.getAccounts();
		Account account = accounts.iterator().next();
		return chargeOff4AcctItem(acctItem, account);
	}
}
